home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Floppyshop 2
/
Floppyshop - 2.zip
/
Floppyshop - 2.iso
/
art&graf.ix
/
art-0039
/
source
/
qsort.mod
< prev
Wrap
Text File
|
1997-04-16
|
13KB
|
330 lines
IMPLEMENTATION MODULE QSort;
(*---------------------------------------------------------------------*)
(* A Generic Sorting Module - based on code taken from the *)
(* book - Software Development with Modula-2 by Ford & Weiner. *)
(* *)
(* The sort procedure will allow the user to to provide the *)
(* compare procedure or will sort using standard data types. *)
(* Multiple keys will NOT be allowed. *)
(* *)
(* The standard data types allowed for sorting will be: *)
(* CARDINAL, INTEGER, REAL, STRING ( Null terminated ), *)
(* LONGCARD, & LONGINT. *)
(* *)
(* The objects to be sorted are considered to be an array of BYTES, *)
(* each object being of fixed length. *)
(* *)
(* The array will be sorted in place. *)
(* *)
(* 1/ 9/89 LGM : Original *)
(*---------------------------------------------------------------------*)
FROM SYSTEM IMPORT BYTE, ADDRESS, ADR, TSIZE;
FROM FastMove IMPORT Swap;
(* IMPORT Trace; (* debugging *) *)
(*----------------------------------------------------------------------*)
(* G L O B A L C O N S T A N T S *)
(*----------------------------------------------------------------------*)
CONST
CSelSortPartitionSize = LONGCARD(7);
(*----------------------------------------------------------------------*)
(* G L O B A L V A R I A B L E S *)
(*----------------------------------------------------------------------*)
VAR
ObjectSize : LONGCARD; (* Size of ONE entry in BYTEs *)
PartitionSizeLimit : LONGCARD; (* Limit for Selection sort *)
ObjectArrayAddr : ADDRESS; (* Where array to be sorted *)
(* starts in memory. *)
InOrder : CompareProc;
(*----------------------------------------------------------------------*)
(* S T A R T O F C O M P A R E P R O C E D U R E S *)
(*----------------------------------------------------------------------*)
(*----------------------------------------------------------------------*)
(* Compare reals: return TRUE if r1 < r2 *)
(*----------------------------------------------------------------------*)
PROCEDURE RealCompare ( a1, a2 : ADDRESS ) : BOOLEAN;
TYPE rptr = POINTER TO REAL;
VAR r1,r2 : rptr;
BEGIN
r1 := a1; r2 := a2;
RETURN ( r1^ < r2^ );
END RealCompare;
(*----------------------------------------------------------------------*)
(* Compare Cardinal: return TRUE if c1 < c2 *)
(*----------------------------------------------------------------------*)
PROCEDURE CardinalCompare( c1, c2 : ADDRESS ) : BOOLEAN;
VAR a1, a2 : POINTER TO CARDINAL;
BEGIN
a1 := c1;
a2 := c2;
RETURN ( a1^ < a2^ );
END CardinalCompare;
(*----------------------------------------------------------------------*)
(* Compare Integer : return TRUE if i1 < i2 *)
(*----------------------------------------------------------------------*)
PROCEDURE IntegerCompare( i1, i2 : ADDRESS ) : BOOLEAN;
VAR a1, a2 : POINTER TO INTEGER;
BEGIN
a1 := i1;
a2 := i2;
RETURN ( a1^ < a2^ );
END IntegerCompare;
(*----------------------------------------------------------------------*)
(* Compare LongInt : return TRUE if li1 < li2 *)
(*----------------------------------------------------------------------*)
PROCEDURE LongIntCompare( li1, li2 : ADDRESS ) : BOOLEAN;
VAR a1, a2 : POINTER TO LONGINT;
BEGIN
a1 := li1;
a2 := li2;
RETURN ( a1^ < a2^ );
END LongIntCompare;
(*----------------------------------------------------------------------*)
(* Compare LongCard : return TRUE if ci1 < ci2 *)
(*----------------------------------------------------------------------*)
PROCEDURE LongCardCompare( lc1, lc2 : ADDRESS ) : BOOLEAN;
VAR a1, a2 : POINTER TO LONGCARD;
BEGIN
a1 := lc1;
a2 := lc2;
RETURN ( a1^ < a2^ );
END LongCardCompare;
(*----------------------------------------------------------------------*)
(* E N D O F C O M P A R E P R O C E D U R E S *)
(*----------------------------------------------------------------------*)
(*----------------------------------------------------------------------*)
(* Which compare procedure do we use *)
(*----------------------------------------------------------------------*)
PROCEDURE GetCompareProc ( dt : SortKeyType;
VAR cp : CompareProc ) : BOOLEAN;
VAR f : BOOLEAN;
BEGIN
f := TRUE;
CASE dt OF
cardinal : cp := CardinalCompare;
ObjectSize := TSIZE(CARDINAL); |
integer : cp := IntegerCompare;
ObjectSize := TSIZE(INTEGER); |
real : cp := RealCompare;
ObjectSize := TSIZE(REAL); |
longcard : cp := LongCardCompare;
ObjectSize := TSIZE(LONGCARD); |
longint : cp := LongIntCompare;
ObjectSize := TSIZE(LONGINT); |
ELSE
f := FALSE;
END; (* case *)
RETURN f;
END GetCompareProc;
(*----------------------------------------------------------------------*)
(* S T A R T O F S O R T P R O C E D U R E S *)
(* *)
(*----------------------------------------------------------------------*)
(*----------------------------------------------------------------------*)
(* Use Selection sort for small partitions - it is faster than quicksort*)
(*----------------------------------------------------------------------*)
PROCEDURE SelectionSort ( StartRecPtr, EndRecPtr : ADDRESS );
VAR MaxRecPtr, SURecPtr, SDRecPtr : ADDRESS;
li : LONGINT;
BEGIN
li := LONGINT(EndRecPtr) - LONGINT(StartRecPtr);
IF li < LONGINT(ObjectSize) THEN
RETURN (* nowt to do *)
END;
SDRecPtr := EndRecPtr;
WHILE ( LONGCARD(SDRecPtr) > LONGCARD(StartRecPtr) ) DO
(* for each record *)
SURecPtr := StartRecPtr;
MaxRecPtr := SDRecPtr;
WHILE ( LONGCARD(SURecPtr) < LONGCARD(SDRecPtr) ) DO
IF InOrder(MaxRecPtr,SURecPtr) THEN (* SUrec is Current Max rec *)
MaxRecPtr := SURecPtr;
END; (* if *)
INC(SURecPtr,ObjectSize);
END; (* while *)
Swap(SDRecPtr,MaxRecPtr,SHORT(ObjectSize));
DEC(SDRecPtr,ObjectSize);
END; (* while *)
END SelectionSort;
(*----------------------------------------------------------------------*)
(* Given a partition then check the first, last and middle element. *)
(* move the median value to the start of the partition. This gives a *)
(* better estimate for the 'pivot' value. *)
(*----------------------------------------------------------------------*)
PROCEDURE SetMedianToStart ( pstart, pend : ADDRESS );
VAR psaddr, peaddr, pmaddr : ADDRESS;
middle : LONGCARD;
BEGIN
psaddr := pstart;
peaddr := pend;
middle := (LONGCARD(pend) - LONGCARD(pstart)) DIV LONGCARD(ObjectSize);
middle := middle DIV 2;
pmaddr := ADDRESS(LONGCARD(pstart) + (middle * ObjectSize));
IF InOrder(psaddr,pmaddr) THEN (* start < middle *)
IF InOrder(pmaddr,peaddr) THEN (* middle < end *)
Swap(pmaddr,psaddr,SHORT(ObjectSize)); (* start < mid < end *)
ELSIF InOrder(psaddr,peaddr) THEN
Swap(peaddr,psaddr,SHORT(ObjectSize)); (* start < end < mid *)
END; (* if *)
ELSE (* middle < start *)
IF InOrder(peaddr,pmaddr) THEN (* end < middle *)
Swap(pmaddr,psaddr,SHORT(ObjectSize)); (* end < mid < str *)
ELSIF InOrder(peaddr,psaddr) THEN (* end < start *)
Swap(peaddr,psaddr,SHORT(ObjectSize)); (* end < mid < str *)
END; (* if *)
END; (* if *)
END SetMedianToStart;
(*----------------------------------------------------------------------*)
(* Sort a Partition, This will move the elements about the pivot. *)
(*----------------------------------------------------------------------*)
PROCEDURE SortPartition ( pstart, pend : ADDRESS ) : ADDRESS;
VAR PivotPtr,
SURecPtr, SDRecPtr : ADDRESS;
BEGIN
SetMedianToStart(pstart,pend);
SURecPtr := pstart;
INC(SURecPtr,ObjectSize);
SDRecPtr := pend;
PivotPtr := pstart;
WHILE ( LONGCARD(SURecPtr) <= LONGCARD(SDRecPtr) ) DO
WHILE ( LONGCARD(SDRecPtr) > LONGCARD(PivotPtr) ) (* scan down for < pivot *)
AND NOT InOrder(SDRecPtr,PivotPtr) DO
DEC(SDRecPtr,ObjectSize);
END; (* while *)
IF ( LONGCARD(SDRecPtr) > LONGCARD(PivotPtr) ) THEN
Swap(SDRecPtr,PivotPtr,SHORT(ObjectSize));
PivotPtr := SDRecPtr;
DEC(SDRecPtr,ObjectSize);
END;
WHILE ( LONGCARD(SURecPtr) < LONGCARD(PivotPtr) )
AND InOrder(SURecPtr,PivotPtr) DO (* scanup for >= pivot *)
INC(SURecPtr,ObjectSize); (* next element to check for > than *)
END; (* while *)
IF ( LONGCARD(SURecPtr) < LONGCARD(PivotPtr) ) THEN
Swap(SURecPtr,PivotPtr,SHORT(ObjectSize));
PivotPtr := SURecPtr;
INC(SURecPtr,ObjectSize);
END;
END; (* while *)
RETURN PivotPtr;
END SortPartition;
(*----------------------------------------------------------------------*)
(* Sort an array, this is recursive *)
(*----------------------------------------------------------------------*)
PROCEDURE Sort ( pstart, pend : ADDRESS );
VAR PivotPtr : ADDRESS;
lowerpartsize, upperpartsize : LONGINT;
LowerEndPtr, UpperStartPtr : ADDRESS;
BEGIN
IF ( LONGCARD(pend) - LONGCARD(pstart) ) < PartitionSizeLimit THEN
SelectionSort( pstart, pend );
RETURN;
END; (* if *)
PivotPtr := SortPartition(pstart,pend);
lowerpartsize := LONGINT(PivotPtr) - LONGINT(pstart);
upperpartsize := LONGINT(pend) - LONGINT(PivotPtr);
LowerEndPtr := PivotPtr;
DEC(LowerEndPtr,ObjectSize);
UpperStartPtr := PivotPtr;
INC(UpperStartPtr,ObjectSize);
IF lowerpartsize < upperpartsize THEN (* sort smaller first *)
IF LONGCARD(pstart) < LONGCARD(LowerEndPtr) THEN
Sort( pstart, LowerEndPtr);
END;
IF LONGCARD(UpperStartPtr) < LONGCARD(pend) THEN
Sort( UpperStartPtr,pend);
END;
ELSE
IF LONGCARD(UpperStartPtr) < LONGCARD(pend) THEN
Sort( UpperStartPtr,pend);
END;
IF LONGCARD(pstart) < LONGCARD(LowerEndPtr) THEN
Sort( pstart, LowerEndPtr);
END;
END;
END Sort;
PROCEDURE SortArray
( VAR ObjectArray : ARRAY OF BYTE;
NumberOfElements : LONGCARD;
TypeOfDataInKey : SortKeyType );
VAR arrayend : ADDRESS;
arraysize:LONGCARD;
BEGIN
ObjectArrayAddr := ADR(ObjectArray);
PartitionSizeLimit := CSelSortPartitionSize * ObjectSize;
IF NOT GetCompareProc(TypeOfDataInKey,InOrder) THEN HALT END;
arraysize := NumberOfElements * ObjectSize ;
arrayend := ADDRESS(arraysize + LONGCARD(ObjectArrayAddr));
DEC(arrayend,ObjectSize);
Sort(ObjectArrayAddr,arrayend);
END SortArray;
PROCEDURE SortArrayWithKeys
( VAR ObjectArray : ARRAY OF BYTE;
VAR ExampleObject : ARRAY OF BYTE;
NumberOfElements : LONGCARD;
UserCompare : CompareProc );
VAR arrayend : ADDRESS;
arraysize: LONGCARD;
BEGIN
ObjectArrayAddr := ADR(ObjectArray);
ObjectSize := HIGH(ExampleObject) + 1;
PartitionSizeLimit := CSelSortPartitionSize * ObjectSize;
InOrder := UserCompare;
arraysize := NumberOfElements * ObjectSize;
arrayend := ADDRESS(LONGCARD(ObjectArrayAddr) + arraysize);
DEC(arrayend,ObjectSize);
Sort(ObjectArrayAddr,arrayend);
END SortArrayWithKeys;
END QSort.